// We have a lot of c-types in here, stop warning about their names!
#![allow(non_camel_case_types)]

use blk1010_lib::{fill::*, map::Map};

#[no_mangle]
pub extern "C" fn rs_Blk1010FillMaster_new(task_per_frame: u8) -> *mut BlockFillMaster {
    let bfm = BlockFillMaster::new(task_per_frame);
    Box::into_raw(Box::new(bfm))
}

#[no_mangle]
pub extern "C" fn rs_Blk1010FillMaster_free(p_pcs: *mut BlockFillMaster) {
    if !p_pcs.is_null() {
        unsafe {
            let _ = Box::from_raw(p_pcs);
        };
    }
}

#[no_mangle]
pub extern "C" fn rs_Blk1010FillMaster_fill_request(
    p_bfm: *mut BlockFillMaster,
    p_map: *mut Map,
) -> i8 {
    if p_bfm.is_null() || p_map.is_null() {
        return -1;
    }
    // 取结构
    let mut fillman = unsafe { Box::from_raw(p_bfm) };
    let mut map = unsafe { Box::from_raw(p_map) };
    fillman.fill_reset();
    fillman.fill_request(&mut map);
    std::mem::forget(fillman);
    std::mem::forget(map);
    0
}

#[no_mangle]
pub extern "C" fn rs_Blk1010FillMaster_fill_revive(
    p_bfm: *mut BlockFillMaster,
    p_map: *mut Map,
) -> i8 {
    if p_bfm.is_null() || p_map.is_null() {
        return -1;
    }
    // 取结构
    let mut fillman = unsafe { Box::from_raw(p_bfm) };
    let mut map = unsafe { Box::from_raw(p_map) };
    fillman.fill_reset();
    fillman.fill_revive(&mut map);
    std::mem::forget(fillman);
    std::mem::forget(map);
    0
}

#[no_mangle]
pub extern "C" fn rs_Blk1010FillMaster_update(p_bfm: *mut BlockFillMaster, p_out: *mut u8) -> i8 {
    if p_bfm.is_null() || p_out.is_null() {
        return -1;
    }
    let mut ret = 0i8;
    // 取结构
    let mut fillman = unsafe { Box::from_raw(p_bfm) };
    // 要求传入足够的32字节的数据缓冲区
    let outs = unsafe { std::slice::from_raw_parts_mut(p_out, 3usize) };

    fillman.update();
    if fillman.cstate == CalcState::FillOk {
        outs[0] = fillman.ret[0];
        outs[1] = fillman.ret[1];
        outs[2] = fillman.ret[2];
        ret = 3;
    }
    std::mem::forget(fillman);
    ret
}

#[no_mangle]
pub extern "C" fn rs_Blk1010Map_new(row: u8, col: u8) -> *mut Map {
    let bm = Map::new(row, col);
    Box::into_raw(Box::new(bm))
}

#[no_mangle]
pub extern "C" fn rs_Blk1010Map_free(p_pcs: *mut Map) {
    if !p_pcs.is_null() {
        unsafe {
            let _ = Box::from_raw(p_pcs);
        };
    }
}

#[no_mangle]
pub extern "C" fn rs_Blk1010Map_assign(
    p_pcs: *mut Map,
    p_data: *const u8,
    data_len: usize,
    combo: u8,
    score: u32,
    clear_all_score: u32,
) -> i8 {
    if p_pcs.is_null() || p_data.is_null() || data_len == 0 {
        return -1;
    }
    // 取结构
    let mut ps = unsafe { Box::from_raw(p_pcs) };
    // 取数据
    let slice = unsafe { std::slice::from_raw_parts(p_data, data_len as usize) };
    ps.set_grid(slice);
    ps.combo = combo;
    ps.score = score;
    ps.clear_all_score = clear_all_score;
    std::mem::forget(ps);
    return 0;
}

#[no_mangle]
pub extern "C" fn rs_Blk1010Map_assign_by_mapid(
    p_pcs: *mut Map,
    mapid: usize,
    combo: u8,
    score: u32,
) -> i8 {
    if p_pcs.is_null() {
        return -1;
    }
    // 取结构
    let mut ps = unsafe { Box::from_raw(p_pcs) };
    ps.set_grid_byid(mapid);
    ps.combo = combo;
    ps.score = score;
    std::mem::forget(ps);
    return 0;
}

#[no_mangle]
pub extern "C" fn rs_Blk1010Map_is_can_put(
    p_pcs: *mut Map,
    p_data: *const u8,
    data_len: usize,
    bid: u8,
    n: u8,
    m: u8,
) -> bool {
    if p_pcs.is_null() || p_data.is_null() || data_len == 0 {
        return false;
    }
    // 取结构
    let mut ps = unsafe { Box::from_raw(p_pcs) };
    // 取数据
    let slice = unsafe { std::slice::from_raw_parts(p_data, data_len as usize) };
    ps.set_grid(slice);
    let mut is_can_put_blk = false;
    for i in 0..n {
        for j in 0..m {
            if let Ok(is_can_put) = ps.put_down(bid as u8, i, j, true) {
                if is_can_put {
                    is_can_put_blk = true;
                    break;
                }
            } else {
                continue;
            }
        }
    }
    std::mem::forget(ps);
    return is_can_put_blk;
}
